package cn.yotta.user.web.autoconfiguration.filter;


import cn.yotta.common.api.model.Response;
import cn.yotta.user.web.autoconfiguration.context.user.UserContext;
import cn.yotta.user.web.autoconfiguration.properties.WhitePathsProperties;
import cn.yotta.user.web.autoconfiguration.provider.LoginUser;
import cn.yotta.user.web.autoconfiguration.provider.LoginUserLoadProvider;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.util.AntPathMatcher;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Objects;

/**
 * @author wyaoyao
 * @date 2022/11/8 13:37
 * 实现从请求中解析出token信息，设置用户上下文
 */
@Slf4j
public class UserContextFilter implements Filter {

    private final WhitePathsProperties whitePathsProperties;

    private final LoginUserLoadProvider loginUserLoadProvider;

    private final AntPathMatcher matcher = new AntPathMatcher();

    private final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 过滤器顺序，数字越小，越先执行
     */
    public final static Integer ORDER = -9999;


    public UserContextFilter(WhitePathsProperties whitePathsProperties, LoginUserLoadProvider loginUserLoadProvider) {
        this.whitePathsProperties = whitePathsProperties;
        this.loginUserLoadProvider = loginUserLoadProvider;
    }


    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
        try {
            // 不是白名单，解析token，设置用户上下文
            if (!isIgnoreUrl(httpServletRequest)) {
                // 设置用户上下文
                try {
                    initContext(httpServletRequest, httpServletResponse);
                    if (Objects.isNull(UserContext.get())) {
                        // 用户未登录，返回401
                        httpServletResponse.setCharacterEncoding("UTF-8");
                        httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                        httpServletResponse.getWriter().write(objectMapper.writeValueAsString(Response.fail("用户未登录，请登录",HttpServletResponse.SC_UNAUTHORIZED)));
                        return;
                    }
                } catch (Exception e) {
                    // 用户登录失败，返回401
                    log.error("user login by token fail by {}", e.getMessage(), e);
                    httpServletResponse.setCharacterEncoding("UTF-8");
                    httpServletResponse.setContentType("application/json;charset=UTF-8");
                    httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                    httpServletResponse.getWriter().write(objectMapper.writeValueAsString(Response.fail("用户登录失败，请重新登录", HttpServletResponse.SC_UNAUTHORIZED)));
                    return;
                }
            }
            // 放行
            filterChain.doFilter(servletRequest, servletResponse);
        } finally {
            // 清除掉用户登录上下文信息
            UserContext.clear();
        }

    }

    private void initContext(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException {
        final LoginUser loginUser = loginUserLoadProvider.get(httpServletRequest);
        UserContext.set(loginUser);
    }

    protected boolean isIgnoreUrl(HttpServletRequest request) {
        final List<String> ignoreUrls = this.whitePathsProperties.getWhitePaths();
        if (CollectionUtils.isEmpty(ignoreUrls)) {
            return false;
        }
        for (String url : ignoreUrls) {
            if (this.matcher.match(url, request.getServletPath())) {
                return true;
            }
        }
        return false;
    }

}
